//
//  LocationManager.swift
//  EasyDana
//
//  Created by mac on 2024/10/10.
//

import CoreLocation

enum LocationError: Error {
    case authorizationDenied
    case unableToGetLocation
    case unknown
}

struct LocationInfo: Codable {
    let sundown: String
    let immoral: String
    let gratitude: String
    let loser: String
    let annas: String
    let laputa: String
    let strayed: String
    let struldbrugs: String
}

class LocationManager: NSObject {
    static let `default` = LocationManager()
    
    private let manager = CLLocationManager()
    
    private let decoder = CLGeocoder()
    
    private var continuations: [(CheckedContinuation<CLLocation, any Error>)] = []
    
    private var executedIndex: [Int] = []
    
    override init() {
        super.init()
        self.manager.delegate = self
    }
    
    func getLocation() async throws -> CLLocation {
        return try await withCheckedThrowingContinuation { continuation in
            if !CLLocationManager.locationServicesEnabled() {
                continuation.resume(throwing: LocationError.unableToGetLocation)
                return
            }
            if #available(iOS 14.0, *) {
                switch self.manager.authorizationStatus {
                case .notDetermined:
                    self.continuations.append(continuation)
                    self.manager.requestWhenInUseAuthorization()
                    break
                case .restricted, .denied:
                    continuation.resume(throwing: LocationError.authorizationDenied)
                    break
                case .authorizedAlways, .authorizedWhenInUse:
                    self.continuations.append(continuation)
                    self.manager.requestLocation()
                    break
                default:
                    continuation.resume(throwing: LocationError.unknown)
                    break
                }
            } else {
                // Fallback on earlier versions
            }
        }
    }
    
    func getReverseLocation(location: CLLocation) async throws -> LocationInfo {
        return try await withCheckedThrowingContinuation { continuation in
            self.decoder.reverseGeocodeLocation(location) { placemarks, error in
                if (error != nil) {
                    continuation.resume(throwing: error!)
                    return
                } else {
                    if ((placemarks?.count ?? 0) > 0) {
                        let mark = placemarks!.last!
                        let country = mark.country ?? ""
                        let countrycode = mark.isoCountryCode ?? ""
                        let privince = mark.administrativeArea ?? ""
                        let city = mark.locality ?? ""
                        let area = mark.subLocality ?? ""
                        let street = mark.thoroughfare ?? ""
                        continuation.resume(returning: LocationInfo(sundown: privince, immoral: countrycode, gratitude: country, loser: street, annas: "\(location.coordinate.latitude)", laputa: "\(location.coordinate.longitude)", strayed: city, struldbrugs: area))
                    } else {
                        continuation.resume(throwing: LocationError.unknown)
                    }
                }
            }
        }
    }
    
    func uploadLocation(showAlert: Bool = true) {
        Task {
            do {
                let location = try await self.getLocation()
                do {
                    let info = try await self.getReverseLocation(location: location)
                    try await HTTPManager.default.post(path: Api.uploadLocation, param: ["sundown": info.sundown, "immoral": info.immoral, "gratitude": info.gratitude, "loser": info.loser, "annas": info.annas, "laputa": info.laputa, "strayed": info.strayed, "struldbrugs": info.struldbrugs], dataType: Int.self)
                } catch {
                    
                }
            } catch {
                if (showAlert) {
                    if HTTPManager.default.config?.gallant == 1 {
                        return
                    } else if HTTPManager.default.config?.gallant == 2 {
                        //每天仅弹1次
                        let date = Date()
                        let formatter = DateFormatter()
                        formatter.dateFormat = "YYYY-MM-dd"
                        let dateStr = formatter.string(from: date)
                        let showed = UserDefaults.standard.bool(forKey: dateStr)
                        if showed == false {
                            DispatchQueue.main.async {
                                let privacyVC = PrivacyEnableViewController()
                                UIViewController.top?.alert(viewController: privacyVC)
                            }
                            UserDefaults.standard.set(true, forKey: dateStr)
                        }
                    }
                }
            }
        }
    }
    
}

extension LocationManager: CLLocationManagerDelegate {
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        if let location = locations.last {
            for i in 0 ..< self.continuations.count {
                if !self.executedIndex.contains(where: {$0 == i}) {
                    self.executedIndex.append(i)
                    let continuation = self.continuations[i]
                    continuation.resume(returning: location)
                }
            }
        } else {
            for i in 0 ..< self.continuations.count {
                if !self.executedIndex.contains(where: {$0 == i}) {
                    self.executedIndex.append(i)
                    let continuation = self.continuations[i]
                    continuation.resume(throwing: LocationError.unableToGetLocation)
                }
            }
        }
    }
    
    func locationManager(_ manager: CLLocationManager, didFailWithError error: any Error) {
        for i in 0 ..< self.continuations.count {
            if !self.executedIndex.contains(where: {$0 == i}) {
                self.executedIndex.append(i)
                let continuation = self.continuations[i]
                continuation.resume(throwing: LocationError.unableToGetLocation)
            }
        }
    }
    
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        if #available(iOS 14.0, *) {
            handleAuthroization(status: manager.authorizationStatus)
        }
    }
    
    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        handleAuthroization(status: status)
    }
    
    private func handleAuthroization(status: CLAuthorizationStatus) {
        switch status {
        case .authorizedAlways, .authorizedWhenInUse:
            self.manager.requestLocation()
            break
        default:
            for i in 0 ..< self.continuations.count {
                if !self.executedIndex.contains(where: {$0 == i}) {
                    self.executedIndex.append(i)
                    let continuation = self.continuations[i]
                    continuation.resume(throwing: LocationError.unableToGetLocation)
                }
            }
            break
        }
    }
}
